package simpletrace

import (
	"bytes"
	"context"
	"embed"
	"encoding/json"
	"fmt"
	"strings"
	"text/template"
	"time"

	"github.com/chenhg5/simpletrace/boolexpr"
)

type HTMLRendererOption struct {
	HideZeroSpan    bool   `json:"hide_zero_span,omitempty"`    // 是否隐藏耗时为0的节点
	MinDurationMs   int    `json:"min_duration_ms,omitempty"`   // 最小持续时间，单位毫秒
	FuncNameRegex   string `json:"func_name_regex,omitempty"`   // 函数名正则过滤
	ShowLongestPath bool   `json:"show_longest_path,omitempty"` // 是否显示最长路径
	FilterSvc       string `json:"filter_svc,omitempty"`        // 服务过滤
	FromSpanID      string `json:"from_span_id,omitempty"`      // 起始节点的 SpanID，默认为根节点
}

type HTMLRenderer struct {
	tmpl *template.Template
}

//go:embed templates/*.html
var templatesFS embed.FS

func NewHTMLRenderer() *HTMLRenderer {
	return &HTMLRenderer{tmpl: template.Must(template.ParseFS(templatesFS, "templates/*.html"))}
}

func (r *HTMLRenderer) Tmpl() *template.Template {
	return r.tmpl
}

type BriefSpanInfo struct {
	SpanID   string           `json:"span_id"`
	Name     string           `json:"name"`
	Children []*BriefSpanInfo `json:"children,omitempty"`
}

func (r *HTMLRenderer) Display(ctx context.Context, span *Span, opt any) (string, error) {

	req, _ := opt.(*HTMLRendererOption)

	fromNode := span

	if req.FromSpanID != "" {
		// 如果指定了起始节点，则查找对应的节点
		if node := findNodeBySpanID(span, req.FromSpanID); node != nil {
			fromNode = node
		} else {
			return fmt.Sprintf("<p>Span ID %s not found in trace.</p>", req.FromSpanID), nil
		}
	}

	var mermaidBuilder strings.Builder
	nodeID := 1
	idMap := map[*Span]string{}
	traceMeta := map[string]map[string]interface{}{}

	// 计算最长路径
	longestPath := findLongestPath(fromNode)

	// 用于判断是否为最长路径
	longestPathSet := make(map[*Span]bool)
	for _, node := range longestPath {
		longestPathSet[node] = true
	}

	var buildGraph func(node *Span, parentID string)

	spanInfos := make(map[string]*BriefSpanInfo)

	buildGraph = func(node *Span, parentID string) {
		duration := node.End.Sub(node.Start).Truncate(time.Millisecond)
		if req.HideZeroSpan && duration.String() == "0s" {
			return // 如果隐藏耗时为0的节点，则跳过
		}
		if req.MinDurationMs > 0 && duration.Milliseconds() < int64(req.MinDurationMs) {
			return // 如果小于最小持续时间，则跳过
		}
		// 正则不匹配主节点
		if req.FuncNameRegex != "" && node.SpanID != "1" {
			if !boolexpr.CompileMatcher(req.FuncNameRegex)(node.Name) {
				return
			}
		}
		if req.FilterSvc != "" && node.ExtraInfo["svc"].(string) != req.FilterSvc {
			return // 如果服务名不匹配过滤条件，则跳过
		}

		id := fmt.Sprintf("N%d", nodeID)
		nodeID++
		idMap[node] = id

		label := fmt.Sprintf("%s<br>%s", node.Name, node.End.Sub(node.Start).Truncate(time.Millisecond))
		mermaidBuilder.WriteString(fmt.Sprintf("  %s[\"%s\"]:::node\n", id, label))

		if node.HasError {
			mermaidBuilder.WriteString(fmt.Sprintf("  style %s fill:#c94d4d,stroke:#0a001e,stroke-width:2px\n", id))
		} else if longestPathSet[node] {
			mermaidBuilder.WriteString(fmt.Sprintf("  style %s fill:#b7b7c9,stroke:#0a001e,stroke-width:2px\n", id))
		} else if strings.HasPrefix(node.Name, "/") {
			mermaidBuilder.WriteString(fmt.Sprintf("  style %s fill:#f9f,stroke:#333,stroke-width:2px\n", id))
		} else if strings.EqualFold(node.ExtraInfo["region"].(string), "jp") {
			mermaidBuilder.WriteString(fmt.Sprintf("  style %s fill:#d1d1ff,stroke:#0a001e,stroke-width:2px\n", id))
		} else if !node.IsAttached {
			mermaidBuilder.WriteString(fmt.Sprintf("  style %s fill:#9292ff,stroke:#0a001e,stroke-width:2px\n", id))
		}

		// click 事件
		mermaidBuilder.WriteString(fmt.Sprintf("  click %s call showDetail(\"%s\")\n", id, node.SpanID))

		// 元数据
		traceMeta[node.SpanID] = map[string]interface{}{
			"name":     node.Name,
			"pos":      node.Pos,
			"duration": duration.String(),
			"args":     node.Args,
			"returns":  node.Returns,
			"svc":      node.ExtraInfo["svc"].(string),
			"region":   node.ExtraInfo["region"].(string),
		}

		if parentID != "" {
			mermaidBuilder.WriteString(fmt.Sprintf("  %s --> %s\n", parentID, id))
		}

		if !node.IsAttached && node.SpanID != fromNode.SpanID {
			return
		}

		// 记录简要信息
		spanInfo := &BriefSpanInfo{
			SpanID: node.SpanID,
			Name:   node.Name,
		}
		for _, child := range node.Children {
			childInfo := spanInfos[child.SpanID]
			if childInfo == nil {
				childInfo = &BriefSpanInfo{SpanID: child.SpanID, Name: child.Name}
				spanInfos[child.SpanID] = childInfo
			}
			spanInfo.Children = append(spanInfo.Children, childInfo)
		}
		spanInfos[node.SpanID] = spanInfo

		for _, child := range node.Children {
			buildGraph(child, id)
		}
	}

	buildGraph(fromNode, "")

	// 输出HTML
	traceMetaJSON, _ := json.Marshal(traceMeta)
	traceInfoJSON, _ := json.Marshal(map[string]string{
		"start_time": fromNode.Start.Format("2006-01-02 15:04:05"),
		"end_time":   fromNode.End.Format("2006-01-02 15:04:05"),
	})
	spanInfosJSON, _ := json.Marshal(spanInfos)
	var buf bytes.Buffer

	_ = r.tmpl.ExecuteTemplate(&buf, "detail.html", map[string]string{
		"MermaidGraph": mermaidBuilder.String(),
		"TraceMeta":    string(traceMetaJSON),
		"TraceInfo":    string(traceInfoJSON),
		"SpanInfos":    string(spanInfosJSON),
	})
	return buf.String(), nil
}
